package cn.green.mng.init;

import cn.green.core.adapt.models.field.OrmField;
import cn.green.core.adapt.models.field.abs.AbsOrmField;
import cn.green.core.adapt.models.table.OrmTable;
import cn.green.core.components.database.DBRouter;
import cn.green.core.model.QFieldModel;
import cn.green.core.scanner.QOrmTableScanner;
import cn.green.core.scanner.store.OrmTableStore;
import cn.green.manage.cache.QCache;
import cn.green.manage.service.QDdlService;
import cn.green.mng.curd.QOrmFieldCurd;
import cn.green.mng.curd.QOrmTableCurd;
import cn.green.mng.sql.QOrmDdl;
import cn.green.utils.ColumnUtils;
import cn.green.utils.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wang.yishu
 * @version 1.0
 * @className OrmTableInitializer
 * @description orm表初始化工具
 * @date 2025/5/16 10:21
 */
public class OrmTableInitializer {

    protected final DBRouter dbRouter;
    protected final QOrmTableCurd tableCurd;
    protected final QOrmFieldCurd fieldCurd;
    protected final QCache cache;
    protected final QOrmDdl qOrmDdl;
    protected final QDdlService qDdlService;

    public OrmTableInitializer(DBRouter dbRouter,QOrmTableCurd tableCurd, QOrmFieldCurd fieldCurd, QCache cache, QOrmDdl qOrmDdl,
                               QDdlService qDdlService) {
        this.dbRouter = dbRouter;
        this.tableCurd = tableCurd;
        this.fieldCurd = fieldCurd;
        this.cache = cache;
        this.qOrmDdl = qOrmDdl;
        this.qDdlService = qDdlService;
        ColumnUtils.setOrmFieldCurd(fieldCurd);
    }


    public void initOrmTable() {
        if (!QOrmTableScanner.canInitQOrmTable) {
            return;
        }
        List<OrmTable> allTables = new ArrayList<>();
        //todo 执行所有OrmTable的初始化操作
        Set<String> tableClassNames = QOrmTableScanner.tableClassNames;
        if (!tableClassNames.isEmpty()) {
            for (String tableClassName : tableClassNames) {
                try {
                    Class<?> clazz = Class.forName(tableClassName);
                    OrmTable ormTable = ColumnUtils.buildOrmTable(clazz, true);
                    allTables.add(ormTable);
                    OrmTableStore.add(ormTable);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        //数据库中的表信息
        List<OrmTable> ormTables = this.tableCurd.ormTables();
        ormTables = ormTables == null ? new ArrayList<>() : ormTables;
        //当前项目所被扫描的的所有表信息
        for (OrmTable table : allTables) {
            //开始配置source和scheme
            if (StringUtils.isEmpty(table.getSourceName())) {
                table.setSourceName(this.dbRouter.getMaster().getSourceName());
            }
            if (StringUtils.isEmpty(table.getSchemeName())) {
                table.setSchemeName(this.dbRouter.getMaster().getSchemeName());
            }
        }
        //初始化orm表信息
        /*
            目标，将通过扫描得到的表与表信息库中的表进行一一对比
            找到需要新增的表，需要修改的表，需要删除的表
         */
        //1.获取需要新增的表
        List<OrmTable> needCrtTables = this.getNeedCrtTables(ormTables, allTables);
        //2.获取需要修改的表
        List<TableUpdate> needUpdTables = this.getNeedUpdTables(ormTables, allTables);
        //3.获取需要删除的表
        List<OrmTable> needDelTables = this.getNeedDelTables(ormTables, allTables);
        if (needCrtTables != null && !needCrtTables.isEmpty()) {
            //执行表的初始化
            needCrtTables.forEach(table -> {
                System.out.println("OrmTable ! -> OrmTableInitializer initial table: [" + table.getSourceName() + "." +
                        table.getSchemeName() + "." + table.getTableName() + "], type: [" + table.getClazz().getName()+ "]");
                this.qDdlService.initialTable(table);
            });
        }
        if (needUpdTables != null && !needUpdTables.isEmpty()) {
            //执行表的更新
            needUpdTables.forEach(update -> {
                System.out.println("OrmTable ! -> OrmTableInitializer update table: [" + update.newTable.getSourceName() + "." +
                        update.newTable.getSchemeName() + "." + update.newTable.getTableName() + "], type: [" + update.newTable.getClazz().getName()+ "]");
                this.updateOrmTable(update.oldTable, update.newTable);
                update.newTable.setId(update.oldTable.getId());
                OrmTable ormTable = this.tableCurd.updVersion(update.newTable);//更新版本号]
                this.cache.setTableToCache(ormTable);
            });
        }

        if (needDelTables != null && !needDelTables.isEmpty()) {
            //执行表的删除
            needDelTables.forEach(table -> {
                System.out.println("OrmTable ! -> OrmTableInitializer delete table: [" + table.getSourceName() + "." +
                        table.getSchemeName() + "." + table.getTableName() + "]");
                this.qDdlService.dropTable(table, true);
            });
        }
    }

    private List<OrmTable> getNeedCrtTables(List<OrmTable> olds, List<OrmTable> news) {
        if (news == null || news.isEmpty()) {
            return null;
        }
        if (olds == null || olds.isEmpty()) {
            return news;
        }
        List<OrmTable> needCrtTables = new ArrayList<>();
        for (OrmTable ormTable : news) {
            String sourceName = ormTable.getSourceName();
            String schemeName = ormTable.getSchemeName();
            String tableName = ormTable.getTableName();
            String oldTableName = ormTable.getOldTableName();
            if (olds.stream().noneMatch(item -> {
                boolean sourceEq = item.getSourceName().equals(sourceName);
                boolean schemeEq = item.getSchemeName().equals(schemeName);
                boolean nameEq = item.getTableName().equals(tableName);
                if (oldTableName != null && !oldTableName.isEmpty()) {
                    if (!nameEq) {
                        nameEq = oldTableName.equals(item.getTableName());
                    }
                }
                return sourceEq && schemeEq && nameEq;
            })) {
                needCrtTables.add(ormTable);
            }
        }
        return needCrtTables;
    }

    private List<TableUpdate> getNeedUpdTables(List<OrmTable> olds, List<OrmTable> news) {
        if (olds == null || olds.isEmpty() || news == null || news.isEmpty()) {
            return null;
        }
        List<TableUpdate> needUpdTables = new ArrayList<>();
        for (OrmTable ormTable : news) {
            String sourceName = ormTable.getSourceName();
            String schemeName = ormTable.getSchemeName();
            String tableName = ormTable.getTableName();
            String oldTableName = ormTable.getOldTableName();
            String version = ormTable.getVersion();
            List<OrmTable> collect = olds.stream().filter(item -> {
                boolean sourceEq = item.getSourceName().equals(sourceName);
                boolean schemeEq = item.getSchemeName().equals(schemeName);
                boolean nameEq = item.getTableName().equals(tableName);
                if (oldTableName != null && !oldTableName.isEmpty()) {
                    if (!nameEq) {
                        nameEq = !oldTableName.equals(item.getTableName());
                    }
                }
                boolean versionNotEq = !version.equals(item.getVersion());
                return sourceEq && schemeEq && nameEq && versionNotEq;
            }).collect(Collectors.toList());
            if (!collect.isEmpty()) {
                TableUpdate tableUpdate = new TableUpdate();
                tableUpdate.oldTable = collect.get(0);
                tableUpdate.newTable = ormTable;
                needUpdTables.add(tableUpdate);
            }

        }
        return needUpdTables;
    }

    private List<OrmTable> getNeedDelTables(List<OrmTable> olds, List<OrmTable> news) {
        if (olds == null || olds.isEmpty()) {
            return null;
        }
        if (news == null || news.isEmpty()) {
            return olds;
        }
        List<OrmTable> needDelTables = new ArrayList<>();
        for (OrmTable ormTable : olds) {
            String sourceName = ormTable.getSourceName();
            String schemeName = ormTable.getSchemeName();
            String tableName = ormTable.getTableName();
            if (news.stream().noneMatch(item -> {
                boolean sourceEq = sourceName.equals(item.getSourceName());
                boolean schemeEq = schemeName.equals(item.getSchemeName());
                boolean nameEq = tableName.equals(item.getTableName());
                if (!nameEq) {
                    nameEq = item.getOldTableName().equals(item.getTableName());
                }
                return sourceEq && schemeEq && nameEq;
            })) {
                needDelTables.add(ormTable);
            }
        }
        return needDelTables;
    }

    /**
     * 修改表
     * @param oldTable  旧表
     * @param newTable  新表
     */
    private void updateOrmTable(OrmTable oldTable, OrmTable newTable) {
        String oldTableName = newTable.getOldTableName();
        String newTableName = newTable.getTableName();
        //查询当前的orm类型的字段
        List<QFieldModel> oldFields = this.fieldCurd.findFieldsByTableName(oldTable.getSourceName(), oldTable.getSchemeName(), oldTable.getTableName());
        if (oldFields == null) {
            oldFields = new ArrayList<>();
        }
        List<QFieldModel> newFields = newTable.allFields();
        if (oldTable.getTableName().equals(oldTableName) && !oldTable.getTableName().equals(newTableName)) {
            //更改名称和注释
            this.qDdlService.updateTable(newTable);
        }
        //表字段对比及修改
        //需要创建的表字段
        List<QFieldModel> needCrtFields = this.getNeedCrtFields(oldFields, newFields);
        if (needCrtFields != null && !needCrtFields.isEmpty()) {
            needCrtFields.forEach(this.qDdlService::addField);
        }
        //需要更新的表字段
        List<FieldOperator> needUpdFields = this.getNeedUpdFields(oldFields, newFields);
        if (needUpdFields != null && !needUpdFields.isEmpty()) {
            needUpdFields.forEach(item -> {
                this.qDdlService.updField(item.newField, true);
            });
        }
        //需要删除的表字段
        List<QFieldModel> needDelFields = this.getNeedDelFields(oldFields, newFields);
        if (needDelFields != null && !needDelFields.isEmpty()) {
            needDelFields.forEach(item -> {
                this.qDdlService.delField(item, true);
            });
        }
    }

    private List<QFieldModel> getNeedCrtFields(List<QFieldModel> olds, List<QFieldModel> news) {
        if (news == null || news.isEmpty()) {
            return null;
        }
        if (olds == null || olds.isEmpty()) {
            return news;
        }
        List<QFieldModel> needCrtFields = new ArrayList<>();
        for (QFieldModel field : news) {
            String fieldName = field.getFieldName();
            String oldFieldName = field.getOldFieldName();
            if (olds.stream().noneMatch(old ->
                    old.getFieldName().equals(fieldName) || (StringUtils.isEmpty(oldFieldName) && old.getFieldName().equals(oldFieldName)))
            ) {
                needCrtFields.add(field);
            }
        }
        return needCrtFields;
    }

    private List<FieldOperator> getNeedUpdFields(List<QFieldModel> olds, List<QFieldModel> news) {
        if (news == null || news.isEmpty()) {
            return null;
        }
        if (olds == null || olds.isEmpty()) {
            return null;
        }
        List<FieldOperator> needUpdFields = new ArrayList<>();
        olds.forEach(oldField -> {
            List<QFieldModel> collect = news.stream().filter(newItem ->
                    oldField.getFieldName().equals(newItem.getFieldName()) || oldField.getFieldName().equals(newItem.getOldFieldName())).collect(Collectors.toList());
            if (!collect.isEmpty()) {
                QFieldModel newField = collect.get(0);
                //todo 需要修改
                needUpdFields.add(this.compare(oldField, newField));
            }
        });
        return needUpdFields;
    }

    //比较
    private FieldOperator compare(QFieldModel oldField, QFieldModel newField) {
        FieldOperator fieldOperator = new FieldOperator();
        fieldOperator.newField = newField;
        return fieldOperator;
    }

    private List<QFieldModel> getNeedDelFields(List<QFieldModel> olds, List<QFieldModel> news) {
        if (olds == null || olds.isEmpty()) {
            return null;
        }
        if (news == null || news.isEmpty()) {
            return olds;
        }
        List<QFieldModel> needDelFields = new ArrayList<>();
        for (QFieldModel field : olds) {
            String fieldName = field.getFieldName();
            if (news.stream().noneMatch(n -> fieldName.equals(n.getFieldName()) || fieldName.equals(n.getOldFieldName()))) {
                needDelFields.add(field);
            }
        }
        return needDelFields;
    }


    private static class TableUpdate {
        private OrmTable oldTable;
        private OrmTable newTable;
    }

    private static class FieldOperator{
        private QFieldModel newField;
    }

}
